//
//  GDataXMLNode+XHTML.m
//  XMLApp
//
//  Created by Nguyen .a.Hph on 3/4/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "GDataXMLNode+XHTML.h"

static const int kGDataHTMLParseOptions = (HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR);

@implementation GDataXMLNode (XHTML)

- (NSString *)innerXML{
    if (xmlNode_ != NULL){
        //return [NSString stringWithCString:(void *)xmlNode_->content encoding:NSUTF8StringEncoding]; //(NSString *)xmlNode_->content;
        //return [NSString stringWithCString:(void *)xmlNodeGetContent(xmlNode_) encoding:NSUTF8StringEncoding];
        NSMutableString *result = [NSMutableString stringWithString:@""];
        NSArray *children = [self children];
        for(GDataXMLNode *child in children)
            [result appendString:[child XMLString]];
        return result;
    }
    return @"";
}

-(NSString *)innerText{
    NSCharacterSet *ws = [NSCharacterSet whitespaceAndNewlineCharacterSet];
    NSMutableString *result = [NSMutableString stringWithString:@""];
    //[result appendString:[[self stringValue] stringByTrimmingCharactersInSet:ws]];
    NSArray *children = [self children];
    for(GDataXMLNode *child in children){
        if ([[[child stringValue] stringByTrimmingCharactersInSet:ws] isEqualToString:@""])
            continue;
        if (![result isEqualToString:@""])
            [result appendString:@" "];
        [result appendString:[[child stringValue] stringByTrimmingCharactersInSet:ws]];
    }
    return result;
}

- (GDataXMLNode *)nextSibling{
    if (xmlNode_ != NULL && xmlNode_->next!=NULL){
        GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:xmlNode_->next];
        return node;
    }
    return nil;
}

- (GDataXMLNode *)previousSibling{
    if (xmlNode_ != NULL && xmlNode_->prev!=NULL){
        GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:xmlNode_->prev];
        return node;
    }
    return nil;
}

- (GDataXMLNode *)parent{
    if (xmlNode_ != NULL && xmlNode_->parent!=NULL){
        GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:xmlNode_->parent];
        return node;
    }
    return nil;
}

- (NSString *)HTMLString {
    
    NSString *str = nil;
    
    if (xmlNode_ != NULL) {
        
        xmlBufferPtr buff = xmlBufferCreate();
        if (buff) {
            
            xmlDocPtr doc = NULL;
            //int level = 0;
            //int format = 0;
            
            //int result = xmlNodeDump(buff, doc, xmlNode_, level, format);
            int result = htmlNodeDump(buff, doc, xmlNode_);
            
            if (result > -1) {
                str = [[NSString alloc] initWithBytes:(xmlBufferContent(buff))
                                                length:(xmlBufferLength(buff))
                                              encoding:NSUTF8StringEncoding];
            }
            xmlBufferFree(buff);
        }
    }
    
    // remove leading and trailing whitespace
    NSCharacterSet *ws = [NSCharacterSet whitespaceAndNewlineCharacterSet];
    NSString *trimmed = [str stringByTrimmingCharactersInSet:ws];
    return trimmed;
}

-(NSString *)innerHTML{
    if (xmlNode_ != NULL){
        NSMutableString *result = [NSMutableString stringWithString:@""];
        NSArray *children = [self children];
        for(GDataXMLNode *child in children)
            [result appendString:[child HTMLString]];
        return result;
    }
    return @"";
}

@end

@implementation GDataXMLElement (XHTML)

- (id)initWithHTMLString:(NSString *)str error:(NSError **)error {
    self = [super init];
    if (self) {
        
        const char *utf8Str = [str UTF8String];
        // NOTE: We are assuming a string length that fits into an int
        xmlDocPtr doc = htmlReadMemory(utf8Str, (int)strlen(utf8Str), NULL, // URL
                                       NULL, // encoding
                                       kGDataHTMLParseOptions);
        if (doc == NULL) {
            if (error) {
                // TODO(grobbins) use xmlSetGenericErrorFunc to capture error
            }
        } else {
            // copy the root node from the doc
            xmlNodePtr root = xmlDocGetRootElement(doc);
            if (root) {
                xmlNode_ = xmlCopyNode(root, 1); // 1: recursive
                shouldFreeXMLNode_ = YES;
            }
            xmlFreeDoc(doc);
        }
        
        
        if (xmlNode_ == NULL) {
            // failure
            if (error) {
                *error = [NSError errorWithDomain:@"com.google.GDataXML HTML"
                                             code:-1
                                         userInfo:nil];
            }
            self = nil;
            //[self release];
            return nil;
        }
    }
    return self;
}

@end

@implementation GDataXMLDocument (XHTML)

- (id)initWithHTMLString:(NSString *)str options:(unsigned int)mask error:(NSError **)error {
    
    NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
    GDataXMLDocument *doc = [self initWithHTMLData:data options:mask error:error];
    return doc;
}

- (id)initWithHTMLData:(NSData *)data options:(unsigned int)mask error:(NSError **)error {
    
    self = [super init];
    if (self) {
        
        const char *baseURL = NULL;
        const char *encoding = NULL;
        
        // NOTE: We are assuming [data length] fits into an int.
        xmlDoc_ = htmlReadMemory((const char*)[data bytes], (int)[data length], baseURL, encoding,
                                 kGDataHTMLParseOptions); // TODO(grobbins) map option values
        if (xmlDoc_ == NULL) {
            if (error) {
                *error = [NSError errorWithDomain:@"com.google.GDataXML HTML"
                                             code:-1
                                         userInfo:nil];
                // TODO(grobbins) use xmlSetGenericErrorFunc to capture error
            }
            //[self release];
            self = nil;
            return nil;
        } else {
            if (error) *error = NULL;
            
            [self addStringsCacheToDoc];
        }
    }
    
    return self;
}

@end
